/*
 * ContBitSelect.java
 * 
 * last update: 15.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */
package engine;

/**
 * This is a BitSelect that continuously assings to its target.
 * Unless BitSelect, it maintains the bits 
 * in an internal BitVector ( data ), 
 * and calls Wire's compute(), in order for it to
 * determine its bits considering all its assignemens;
 */
class ContBitSelect extends BitSelect implements Executable{

	BitVector data;
	byte[] strengths;

	/**
	 * The timestamp of the last programmed update
	 */
	long updateTime;
	boolean active;

	/**
	 * The computed data to be assigned to the target BitVector
	 */
	BitVector newB;
	/**
	 *The computed strengths to be assigned to target
	 */
	byte []newStrengths;

	ContBitSelect(NameSpace ns, BitVector b,
			GenericSelectorDescription target,
			int start, int end, byte strength)
			{
		super(ns, b, target, start, end);
		data = new BitVector(start, end);
		strengths = new byte [length];
		for (int i = 0; i < length; i++)
			strengths[i] = strength;
		this.b = b;
		b.addAssignement(this);
		try {
			data.attrib(start, end, b, start, end);
		} catch (InterpretTimeException intex) {
			xConsole.debug("InterpretTimeException into ContBitSelect<init>"); 
			/*we do nothing here, because the same exception will
        be raised later and handled accordingly*/
		}
		updateTime = -1;
		active = false;
			}

	/**
	 * The implicit strength (strong1, strong0) is used here.
	 */
	ContBitSelect(NameSpace ns, BitVector b,
			GenericSelectorDescription target,
			int start, int end)
			{
		this (ns, b, target, start, end, Strength.defaultStrength);
			}

	ContBitSelect(NameSpace ns, BitVector b,
			GenericSelectorDescription target,
			byte strength)
			{
		this (ns, b, target, b.msb, b.lsb, strength);
			}

	/**
	 * Called by @see assign with the appropriate parameters
	 */
	synchronized void set(BitVector x, int start, int end)
	throws InterpretTimeException
	//TODO: this recomputation can be optimised to take place only
	//on the start:end range
	{
		xConsole.debug("ContBitSelect.set(" + x + ", " +
				start + ", " + end + "), data is:" + data +
				" data.msb:" + data.msb + " data.lsb:" + data.lsb + " inc: " +
				data.increment);
		Object [] dataRes;
		data.attrib(this.start, this.end, x, start, end);
		updateTime = Time.oClock(); //force compute to take this chunk
		//into account
		dataRes = b.compute();
		newB = (BitVector)dataRes[0];
		if(dataRes.length > 1) { //does the target implements strength-keeping ?
			newStrengths = (byte[])dataRes[1];
			for (int i = 0; i < newStrengths.length; i++)
				xConsole.debug("strength[" + i + "] = " +
						Strength.strengthToString(newStrengths[i],
								newB.getb(i)));
		}
		int delay;

		try{
			Wire w = (Wire) b;
			delay = w.computeTransitionDelayFor(newB);
			xConsole.debug("delay is: " + delay);
		}catch(ClassCastException ex){
			delay = 0;
		}
		if (!active) {
			updateTime = Time.oClock()+delay;
			active = true;
			Time.addFinisher(delay, new GenericInstruction(this));
		}
	}

	/**
	 * First sets the strengths, and then calls the old assign.
	 * This will cause the assigned wire to compute its value
	 * considering the new strengths
	 */
	public void setStrengths(byte[] s, int startIndex)
	{
		xConsole.debug("ContBitSelect.setStrengths");
		for(int i = 0, n = strengths.length; i < n; i++)
			strengths[i] = s[startIndex++];
	}

	public synchronized void execute()
	throws InterpretTimeException, SimulationStoppedException
	{
		if((updateTime == Time.oClock()) && active){ //updateTime > oClock means
			//that another update has 
			//superseeded this one, so skip it
			//active is made false after the first execution,
			//to avoid multiple useless updates (I guess this little optimisation
			//does not introduce bad behaviour).
			if (target != null)
				xConsole.trace(((GenericSelectorDescription)target).fqn +
						" <<< " + data);
			synchronized (b) {
				try{
					Wire w = (Wire)b;
					w.attrib(newB, start, end, newStrengths);
				}catch(ClassCastException cex){
					//seems like  data is a mere reg
					b.attrib(newB);
				}
			}
			active = false;
		}
	}

	/**
	 * @return the strength for the specified index
	 * @param i the index in the ramge start..end
	 */
	public byte strength(int i){
		return start > end ? strengths[i-end] : strengths[end-i];
	}

	/**
	 * @see WireSelection.detach
	 */
	void release() {
		b.removeAssignement(this);
	}

	/**
	 * @see WireSelection.reatach
	 */
	void reatach() {
		b.addAssignement(this);
	}


	/**
	 *  @see WireSelection.reatach
	 */
	void breakSelection(WireSelection bits[], int startIndex) {
		int i = start, inc = start >= end ? -1 : 1;
		while(true) {
			bits[startIndex++] = new ContBitSelect(ns, b, target, i, i, strengths[0]);
			if (i == end) break;
			i += inc;
		}
	}
}
